#include <QCoreApplication>
#include <QTextStream>
#include <QFileInfo>
#include <QDebug>

#include <dlfcn.h>
#include "IQHello.h"
#include "macros.h"
#include <iostream>
#include <QDir>


void testPlugin(const char* libPath) {
    void* handle = dlopen(libPath, RTLD_LAZY);
    if (!handle) {
        std::cerr << "Cannot load library: " << dlerror() << std::endl;
        return;
    }
    
    class IQHelloWrapper : public IQHello {
    public:
        IQHelloWrapper(void *handle) : m_handle(handle) {

            impl_t = (IQImplTypeFunc)dlsym(handle, "impl_t");
            create = DLSYM_CREATE(IQHello);
            destroy = DLSYM_DELETE(IQHello);

            if (!create || !destroy || !impl_t) {
                std::cerr << "Cannot load symbols: " << dlerror() << std::endl;
                dlclose(handle);
                m_isValid = false;
                return;
            }
            m_isValid = true;

            m_hello = create();
            switch (impl_t()) {
            case IQ_IMPL_C:
                std::cout << "C implementation" << std::endl;
                break;
            case IQ_IMPL_CPP:
                std::cout << "C++ implementation" << std::endl;
                break;
            case IQ_IMPL_GO:
                std::cout << "Go implementation" << std::endl;
                break;
            }
        }

        ~IQHelloWrapper() {
            if (m_isValid) {
                destroy(m_hello);
            }
            dlclose(m_handle);
        }

        std::string getHello() override {
            // Cpp implementation
            if (impl_t() == IQ_IMPL_CPP) {
                return m_hello->getHello();
            }

            // C implementation
            auto func = reinterpret_cast<GetHelloFunc>(dlsym(m_handle, "getHello"));
            if (!func) {
                throw std::runtime_error("Failed to load function: " + std::string(dlerror()));
            }
            return std::string(func(m_hello));
        }

        bool isValid() {
            return m_isValid;
        }
    private:
        void* m_handle;
        bool m_isValid;

        IQImplTypeFunc impl_t;
        CIQHelloFunc create;
        DIQHelloFunc destroy;
        
        IQHello* m_hello;
    };

    IQHelloWrapper wapper(handle);
    if (!wapper.isValid()) {
        return;
    }

    std::cout << libPath << " says: " << wapper.getHello() << std::endl;
}

int main(int argc, char *argv[])
{   
    QCoreApplication a(argc, argv);
    auto cd = [](QString path) {
        // QFileInfo info(path);
        QDir::setCurrent(path);
        qDebug() << "Current dir: " << QDir::currentPath();
    };
    cd(a.applicationDirPath());

    testPlugin("./plugins/libhello_c.so");
    testPlugin("./plugins/libhello_cpp.so");
    testPlugin("./plugins/libhello_go.so");
    testPlugin("./plugins/libhello_py.so");
    testPlugin("./plugins/libhello_qt.so");
    testPlugin("./plugins/libhello_rust.so");
    testPlugin("./plugins/libhello_rust_cpp.so");
    return 0;
}
